import { useState, useMemo } from "react"

const UseMemoDemo = () => {
  const [count, setCount] = useState(0)
  const [text, setText] = useState("")
  const [dark, setDark] = useState(false)

  // 模拟计算密集型操作
  const expensiveCalculation = (num: number) => {
    console.log("执行昂贵计算...")
    // 模拟耗时计算
    let result = num
    for (let i = 0; i < 1000000; i++) {
      result += i % 2 === 0 ? 1 : -1
    }
    console.log(result)

    // 保证循环不会被优化掉，但最终只返回num的2倍
    return num * 2
  }

  // 不使用useMemo，每次组件重新渲染都会执行
  const directResult = expensiveCalculation(count)

  // 使用useMemo，只有count改变时才会执行
  const memoizedResult = useMemo(() => {
    return expensiveCalculation(count)
  }, [count])

  // 使用useMemo缓存引用类型的例子
  const themeStyles = useMemo(() => {
    return {
      backgroundColor: dark ? "#333" : "#fff",
      color: dark ? "#fff" : "#333",
      padding: "20px",
      borderRadius: "4px",
      transition: "all 0.3s ease",
    }
  }, [dark])

  const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value)
  }

  return (
    <div className="demo-section">
      <h2>useMemo Hook 面试题讲解</h2>

      <div className="card">
        <h3>1. 什么是 useMemo?</h3>
        <p>
          useMemo 是一个 React
          Hook，用于缓存计算结果，只有在依赖项发生变化时才重新计算。
          它可以帮助避免在每次渲染时执行昂贵的计算，从而优化性能。
        </p>

        <div className="code-example">
          <code>
            const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a, b),
            [a, b]);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>2. 常见面试问题: useMemo 解决了什么问题?</h3>
        <p>useMemo 主要解决两类问题:</p>
        <ol>
          <li>避免每次渲染时重复进行昂贵的计算</li>
          <li>保持引用对象的引用相等性，避免不必要的重新渲染</li>
        </ol>
      </div>

      <div className="card">
        <h3>3. 使用场景: 昂贵计算的性能优化</h3>

        <div className="example-output">
          <p>
            计数: <strong>{count}</strong>
          </p>
          <button onClick={() => setCount((prev) => prev + 1)}>递增计数</button>

          <p>
            直接计算结果 (每次渲染都执行): <strong>{directResult}</strong>
          </p>
          <p>
            使用 useMemo 结果 (仅 count 变化时执行):{" "}
            <strong>{memoizedResult}</strong>
          </p>

          <div className="flex-container">
            <div className="flex-item">
              <p>尝试输入文本 - 会导致组件重新渲染:</p>
              <input
                type="text"
                value={text}
                onChange={handleTextChange}
                placeholder="输入文本..."
              />
            </div>
            <div className="flex-item">
              <p>
                当前输入: <strong>{text || "(空)"}</strong>
              </p>
              <p>
                修改文本时，直接计算会重新执行，而 useMemo
                缓存的计算不会重新执行 (可以在控制台中观察 "执行昂贵计算..."
                日志的差异)
              </p>
            </div>
          </div>
        </div>

        <div className="code-example">
          <code>
            // 不使用 useMemo - 每次渲染都重新计算 const directResult =
            expensiveCalculation(count); // 使用 useMemo - 只有 count
            变化时才重新计算 const memoizedResult = useMemo(() =&gt; {"{"}
            return expensiveCalculation(count);
            {"}"}, [count]);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>4. 使用场景: 保持引用相等</h3>

        <div style={themeStyles}>
          <p>这个区域使用 useMemo 缓存的样式对象</p>
          <p>
            当前主题: <strong>{dark ? "暗色" : "亮色"}</strong>
          </p>
          <button onClick={() => setDark((prev) => !prev)}>切换主题</button>
        </div>

        <div className="code-example">
          <code>
            // 使用 useMemo 缓存对象引用 const themeStyles = useMemo(() =&gt;{" "}
            {"{"}
            return {"{"}
            backgroundColor: dark ? "#333" : "#fff", color: dark ? "#fff" :
            "#333", // ...其他样式
            {"}"};{"}"}, [dark]);
            {"<div style={themeStyles}>...</div>"}
          </code>
        </div>

        <div className="warning">
          <p>
            <strong>重要提示:</strong> 当你向子组件传递对象或函数时，如果不使用
            useMemo 或 useCallback 缓存，
            这些引用类型会在每次渲染时创建新实例，可能导致子组件不必要的重新渲染。
          </p>
        </div>
      </div>

      <div className="card">
        <h3>5. 常见面试问题: useMemo vs useCallback</h3>
        <table>
          <thead>
            <tr>
              <th>Hook</th>
              <th>用途</th>
              <th>缓存内容</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <strong>useMemo</strong>
              </td>
              <td>缓存计算结果或引用类型</td>
              <td>返回值本身</td>
            </tr>
            <tr>
              <td>
                <strong>useCallback</strong>
              </td>
              <td>缓存回调函数</td>
              <td>函数实例</td>
            </tr>
          </tbody>
        </table>

        <div className="code-example">
          <p>useMemo 缓存计算结果:</p>
          <code>
            const memoizedValue = useMemo(() =&gt; computeValue(a, b), [a, b]);
          </code>

          <p>useCallback 缓存函数:</p>
          <code>
            const memoizedCallback = useCallback(() =&gt; {"{"}
            doSomething(a, b);
            {"}"}, [a, b]);
          </code>
        </div>
      </div>

      <div className="card">
        <h3>6. 常见面试问题: 什么时候应该使用 useMemo?</h3>
        <p>useMemo 不应滥用，应考虑以下场景使用:</p>
        <ul>
          <li>计算真正昂贵且明显影响性能的情况</li>
          <li>需要保持引用相等性来避免子组件不必要渲染的情况</li>
          <li>避免重复创建在依赖项没变时不需要重新创建的复杂对象</li>
        </ul>

        <div className="warning">
          <p>
            <strong>注意:</strong> useMemo 本身也有开销，对于简单计算，使用
            useMemo 的开销可能超过直接计算的开销。 过度使用 useMemo
            可能导致内存使用增加，反而损害性能。
          </p>
        </div>
      </div>

      <div className="card">
        <h3>7. useMemo 面试核心要点</h3>
        <ul>
          <li>useMemo 用于缓存计算结果或引用类型值</li>
          <li>只有在依赖项发生变化时，useMemo 才会重新执行回调函数</li>
          <li>useMemo 可以避免昂贵计算和避免不必要的重新渲染</li>
          <li>不应滥用 useMemo，只在需要优化性能的地方使用</li>
          <li>
            React 可能在某些情况下丢弃已缓存的值，所以 useMemo
            不能作为严格的性能保证
          </li>
          <li>在 React.memo 包装的组件内特别有用，可以避免不必要的重新渲染</li>
        </ul>
      </div>
    </div>
  )
}

export default UseMemoDemo
